#ifndef __EPSILON3D__PROGRAM_H__
#define __EPSILON3D__PROGRAM_H__

#include "shader.h" // TODO: Figure out a way to implement GetShader, without
                    //         requiring this include --> (Forward dec.)

class eTB_Program
{
friend class eTB_ProgramFactory;

public:
  bool Bind         (void);
  bool Unbind       (void);

  /* TODO: Allow multiple shaders of the same type to be attached... */
  bool AttachShader (eTB_Shader* pShader);
  bool Link         (void);

  eTB_Shader*
       GetShader (eTB_Shader::Type type);

  eStringA GetLog (void);

  const char* GetName (void) { return name_; }

  // TODO: Abstract uniforms (variables), rather than returning
  //         an API-specific handle...
  GLuint GetUniform (const char* szName);

protected:
  eTB_Program (const char* szName) {
    handle_ = glCreateProgram ();
    name_   = strdup (szName);
  }

  ~eTB_Program (void) {
    glDeleteProgram (handle_);
    
    if (name_ != NULL) { 
      delete [] name_;
      name_ = NULL;
    }
  }

  const char* name_;
  GLuint      handle_;

private:
  eTB_Shader* pixel_shader_;
  eTB_Shader* vertex_shader_;

  /*
  eTB_Shader* geometry_shader_;
  eTB_Shader* hull_shader_;
  eTB_Shader* domain_shader_;
  eTB_Shader* compute_shaer_;
  */
};

class eTB_ProgramFactory
{
public:
  eTB_Program* createProgram (const char* szName);
};

bool CompileAndAttach (eTB_Shader*  pShader, eTB_Program* pProgram);
bool LinkProgram      (eTB_Program* pProgram);

#endif /* __EPSILON3D__PROGRAM_H__ */

